#include "globals.h"
static struct rtt_info   rttinfo;
static int rttinit = 0;

static void sig_alrm(int signo);
void start_timer(struct itimerval* tout_val);
void stop_timer(struct itimerval* tout_val, datagram* recvhdr);
static sigjmp_buf jmpbuf;



int main()
{
    FILE * fp;

    const int on = 1;
    int server_port = 0;
    int serv_win = 0;
    int i = 1;
    int j;
    int ele_count = 0;
    int max;
    int err;
    int rcwin;
    int confd; 

    char buf[MAX_DATAGRAM_BUF_SIZE] = "\0";
    char filename[TMP_BUFSIZE] = "\0";

    struct sockaddr_in cliaddr;
    struct sockaddr_in servaddr;

    struct sock_ele s[TMP_BUFSIZE];

    fd_set rset;

    int flags;
    int client_port;
    char ip_client[INET_ADDRSTRLEN];
    char ip_server[INET_ADDRSTRLEN];
    char *ptr;

    struct sockaddr_in sock_nm;
    socklen_t len;

    datagram dgram;
    datagram_list packet_buffer_head;
    datagram* dg_ptr;
    
    parse_server_in(&server_port, &serv_win);

    ele_count = set_interface_info(s, server_port);

    FD_ZERO(&rset);		

    //
    // Used to handle the SIGCHILD signal
    //

     Signal(SIGCHLD,sig_chld); 	

    while (TRUE)
    {     
        max = 0;
        rcwin = 0;

        for (i = 0; i < ele_count; i++)
        {	
            FD_SET(s[i].sockfd, &rset);

            if(max<s[i].sockfd)
            {
                max=s[i].sockfd;
            }
        }

        bzero(buf, TMP_BUFSIZE);	
        log_msg_int("B4 Select", getpid());

        err = select(max + 1, &rset, NULL, NULL, NULL);

        if (err < 0 && errno == EINTR)
        {
            log_msg("EINTR error");
            continue;
        }
        else if (err < 0)
        {
            err_sys("Select Error %d\n", errno);
        }

        log_msg_int("After Select", getpid());

        flags = 0;

        for (i = 0; i < ele_count; i++)
        {
            if (FD_ISSET(s[i].sockfd, &rset))
            {
                len = sizeof(cliaddr);	

                log_msg_int_int("B4 Recv from", getpid(), s[i].sockfd);

                recvfrom_datagram(s[i].sockfd, buf, flags, (SA*) &cliaddr, &len,
                        &rcwin);
                printf("Client port before fork %d", cliaddr.sin_port); 
                printf("%s File requested is :%s %s\n", SRV_LOG, buf, 
                        s[i].ip_addr);

                strcpy(filename, buf);
                strncpy(ip_client, Sock_ntop((SA*) &cliaddr, len), INET_ADDRSTRLEN);

                ptr = strchr(ip_client, ':');
                *ptr='\0';
                ptr = strchr(Sock_ntop((SA*)&cliaddr, len), ':');

                client_port = atoi((ptr + 1));
                strncpy(ip_server, s[i].ip_addr, INET_ADDRSTRLEN);

                ptr=strchr(ip_server, ':');
                *ptr='\0';	
                break;
            }	
        }	

        if (fork() == 0)
        {
            flags = 0;
            packet_buffer_head.link = 0;
            packet_buffer_head.dgram = (datagram *) malloc(sizeof(datagram));

            packet_buffer_head.dgram->seq_num = -1;

            for (j = 0; j < ele_count; j++)
            {
                if (j != i)
                {
                    close(s[j].sockfd);
                }
            }

            flags = get_route_flags(ip_server, ip_client, s[i].sub_mask);

            if (flags < 0)
            {
                break;
            }

            confd = Socket(AF_INET,SOCK_DGRAM, 0);

            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin_family = AF_INET;
            inet_pton(AF_INET, ip_server, &servaddr.sin_addr);
            servaddr.sin_port = htons(0);
            Bind(confd, (SA *) & servaddr, sizeof(servaddr));

            len = sizeof(sock_nm);
            getsockname(confd, (SA*) &sock_nm, &len);
            sprintf(buf,"%s%s", Sock_ntop((SA*) &sock_nm, len), "\0");

            printf("%s Server Socket created and Binded\n\tIPServer:Portno: %s\n", 
                    SRV_LOG, buf);

            bzero(&cliaddr, sizeof(cliaddr));
            cliaddr.sin_family = AF_INET;
            inet_pton(AF_INET, ip_client, &cliaddr.sin_addr);
            cliaddr.sin_port = htons(client_port);
            Connect(confd, (SA*) &cliaddr, sizeof(cliaddr));

            len = sizeof(cliaddr);

            printf("Client port %d\n", cliaddr.sin_port);
            
            dg_ptr = (datagram*) malloc(sizeof(datagram));

            fp = fopen(filename, "r");

            if (fp == NULL)
            {
                strcpy(buf, "");
            }

            strcpy(dg_ptr->data, buf);
            dg_ptr->wsize = serv_win;
            Sendto(s[i].sockfd, dg_ptr, sizeof(*dg_ptr),flags, (SA*) &cliaddr, len);
            
            if (strcmp(buf,"") == 0)
            {
                printf("Requested file not found: %s\n", filename);
                close(confd);
                exit(0);
            }

            recv_datagram(confd, buf, flags);

            printf("%s Received buf '%s'\n", SRV_LOG, buf);//ACK frm Client

            close(s[i].sockfd);
/*
            fp = fopen(filename, "r");

            if (fp == NULL)
            {
                strcpy(buf, "Cannot open file :: FILE NOT FOUND");
                send_datagram(confd, buf, flags, &packet_buffer_head,
                        QFLAG_DONTQ);	
            }
            else
            {
            */
         /*       while (Fgets(buf, MAX_DATAGRAM_SZ - 1, fp) != NULL)
                {
                    strcat(buf, "\0");
                    
                    log_msg("sending buffer....");
                    log_msg(buf);

                    dg_ptr = prep_datagram(buf); 
	                append_buffer(&packet_buffer_head, dg_ptr);

                   Dg_send_recv (confd, dg_ptr, flags, &packet_buffer_head, NULL);*/
                   server_to_client(fp, confd, dg_ptr, flags,
                           &packet_buffer_head, rcwin, serv_win);
/*               }

                buf[0] = EOF;
                    
                log_msg("sending buffer....");
                log_msg(buf);
                
                dg_ptr = prep_datagram(buf); 
	        append_buffer(&packet_buffer_head, dg_ptr);
                Dg_send_recv (confd, dg_ptr, flags, &packet_buffer_head, NULL);
                 server_to_client(confd, dg_ptr, flags, &packet_buffer_head);                */
                
                fclose(fp);
                close(confd);
		
                // remove_from_buffer(0, &packet_buffer_head);
                // traverse_datagram_buffer(packet_buffer_head);
            //}
	        log_msg("BBye child\n");
            exit(0);
        }
        else
        {
            log_msg_int("Parent... ",getpid());
        }	

    }

    return(0);
}

int* 
prepare_ack_flags(
        datagram_list* head,
        arq_params* win
        )
{
    int i = 0;
    int *acks = (int*) malloc(sizeof(int)*(win->win_size));
    int slide_val = -1;
    //    int max_val = -1;

    reset_ack_flags(acks, win->win_size);

    for(i = win->win_l; i < (win->win_l+win->win_size); i++)
    {
        if (find_datagram(head, i) == NULL)
        {
            acks[i - win->win_l] = 1;
        }
    }

    print_ack_array(acks, win->win_size);
    return acks;
}

int
slide_window(
        datagram_list* head,
        arq_params* win,
        int highest_ack_recvd
        )
{
    int i = 0;
    int acks[win->win_size];
    int slide_val = -1;
    //    int max_val = -1;

    reset_ack_flags(acks, win->win_size);

    //    max_val = max((win->win_l+win->win_size), highest_ack_recvd);
    //    log_msg_int("max val", max_val);

    for(i = win->win_l; i < (win->win_l+win->win_size); i++)
    {
        if (find_datagram(head, i) == NULL)
        {
            log_msg_int("Not found", i);
            acks[i - win->win_l] = 1;
        }
        else
        {
            log_msg_int("Found", i);
        }
    }

    print_ack_array(acks, win->win_size);

    if (should_slide(acks, win->win_size) == TRUE)
    {
        slide_val = slide(win, acks);
        log_msg_int("slide_val", slide_val);

        if (win->win_l < highest_ack_recvd)
        {
            slide_val += (highest_ack_recvd - win->win_l);
            win->win_l += slide_val;
        }

        log_msg_int("slide_val(recalib)", slide_val);

    }

    return slide_val;
}

void 
make_socket_non_blocking(
        int* sockfd
        )
{
    int fg;

    if((fg = fcntl (*sockfd, F_GETFL, 0)) < 0)
    {
        err_sys("F_GETFL error");
    }

    fg |= O_NONBLOCK;

    if( fcntl(*sockfd, F_SETFL, fg) < 0)
    {
        err_sys("F_SETFL error");
    }
}

void 
make_socket_blocking(
        int* sockfd
        )
{
    int fg;

    if((fg = fcntl (*sockfd, F_GETFL, 0)) < 0)
    {
        err_sys("F_GETFL error");
    }

    fg &= (~O_NONBLOCK);

    if( fcntl(*sockfd, F_SETFL, fg) < 0)
    {
        err_sys("F_SETFL error");
    }
}

void
stop_timer(
        struct itimerval* tout_val,
        datagram* recvhdr
        )
{
    tout_val->it_value.tv_sec = 0;
    tout_val->it_value.tv_usec = 0; 

    setitimer(ITIMER_REAL, tout_val,0);

    rtt_stop(&rttinfo, rtt_ts(&rttinfo) - recvhdr->tstamp);
}

void
start_timer(
        struct itimerval* tout_val
        )
{
    int temp_sec = rtt_start(&rttinfo);
    int time_sec = 0;
    int time_m_sec = 0;
    time_t ticks;

    if (temp_sec >= 1000) 
    {
        time_sec = temp_sec / 1000;
    }

    time_m_sec = temp_sec % 1000;

    tout_val->it_interval.tv_sec = 0;
    tout_val->it_interval.tv_usec = 0;
    tout_val->it_value.tv_sec = time_sec + 1;
    tout_val->it_value.tv_usec = time_m_sec;

    ticks = time(NULL);
    printf("Set time out %d, Current time: %.24s\r\n", tout_val->it_value.tv_sec, ctime(&ticks));
    setitimer(ITIMER_REAL, tout_val,0);
}

static void
sig_alrm(int signo)
{
    time_t ticks;
    log_msg("sig alarm");
    ticks = time(NULL);
    printf("SIGALRM, Current time: %.24s\r\n", ctime(&ticks));
    siglongjmp(jmpbuf, 1);
}

void 
server_to_client ( 
        FILE * fp, 
        int confd, 
        datagram* dg_ptr, 
        int flags, 
        datagram_list * packet_buffer_head,
        int rcwin,
        int serv_win
        )
{
    datagram recvhdr;
    datagram_list * temp;
    int n, count = 0;
    int i;
    int recvd_bytes;
    arq_params win;
    char buf[MAX_DATAGRAM_BUF_SIZE];

//    int rcwin = 5;   /*Assumed value ... has to be Advertised by Receiver*/
    int ssthrsh;
    int cwin = 1;
    int sent_count;
    int num_acks_recvd;
    int slide_val = 1;
    int eof_reached = FALSE;
    int cwin_sent_count;
    int cwin_congestion;
    int highest_ack_recvd = -1;

    struct itimerval tout_val;    
    int temp_sec = 0;
    int time_sec = 0;
    int time_m_sec = 0;
    int acks_recvd = 0;

    win.win_l = 0;
    win.win_r = 0;
    win.win_size = 5;

    recvhdr.ack_num = -1;

    if (rttinit == 0) 
    {
        rtt_init(&rttinfo);     // first time we're called
        rttinit = 1;
    }

    Signal(SIGALRM, sig_alrm);

    printf("Recv Win: %d, Sender Win: %d\n", rcwin, serv_win);

    while(1)
    {
sendagain:
        congestion_control( &win, rcwin, &cwin, &ssthrsh, cwin_congestion, serv_win);
        count = win.win_l;

        if (slide_val > 0 && eof_reached == FALSE)
        {
            while( count < (win.win_l + win.win_size) )
            {
                if (find_datagram(packet_buffer_head, count) != NULL)
                {
                    //
                    // Sequence number present in send queue. 
                    //

                    count++;
                    continue;
                }

                if(Fgets(buf, MAX_DATAGRAM_SZ - 1, fp) != NULL)
                {
                    strcat(buf, "\0");
                }
                else
                {
                    buf[0] = EOF;     
                    eof_reached = TRUE;
                    dg_ptr = prep_datagram(buf); 
                    append_buffer(packet_buffer_head, dg_ptr);
                    count++;
                    break;
                }

                dg_ptr = prep_datagram(buf); 
                append_buffer(packet_buffer_head, dg_ptr);
                count++;
            }

            rtt_newpack(&rttinfo);              // initialize for this window

        }

        temp = packet_buffer_head->link;
        sent_count = 0;
        slide_val = -1;

        if (get_last_node(packet_buffer_head) != NULL)
        {
            while(temp != NULL && sent_count < win.win_size)
            {
                dg_ptr = temp->dgram;
                dg_ptr->tstamp = rtt_ts(&rttinfo);

                send(confd, dg_ptr, sizeof(*dg_ptr), flags);
                printf("Packet Sent :: Seq no: %d\n", dg_ptr->seq_num);
                sent_count++;
                temp = temp->link;
            }

            sleep(1);
            start_timer(&tout_val);
        }

        if (sigsetjmp(jmpbuf, 1) != 0 ) 
        {
            if (rtt_timeout(&rttinfo) < 0) 
            {
                err_msg("dg_send_recv: no response from server, giving up");
                rttinit = 0;
                
                //
                // reinit in case we're called again 
                //
                
                errno = ETIMEDOUT;
                return;
            }

            rtt_debug(&rttinfo);
            
            err_msg("dg_send_recv: timeout, retransmitting");
        
            make_socket_blocking(&confd);
            remove_packets(packet_buffer_head, highest_ack_recvd, win);
            log_msg_int("highest_ack_recvd", highest_ack_recvd);
            cwin_congestion = TRUE;

            slide_val = slide_window(packet_buffer_head, &win, highest_ack_recvd);
            printf("Window: LEFT :: %d RIGHT :: %d SIZE :: %d\n\n", win.win_l, win.win_r, win.win_size);

            goto sendagain;
        }
       
        cwin_sent_count = sent_count;
        log_msg_int("cwin_count", cwin_sent_count);

        temp = get_last_node(packet_buffer_head); 
        win.win_r = packet_buffer_head->dgram->seq_num + 1;

        printf("Window: LEFT :: %d RIGHT :: %d SIZE :: %d\n\n", win.win_l, win.win_r, win.win_size);

        n = 0;
        flags = flags | (MSG_PEEK);
       
        acks_recvd = 0;
        do
        {
            //
            // Peek and get the ack data.
            //


            flags = flags | (MSG_PEEK);
            make_socket_non_blocking(&confd);
            recvd_bytes = recv(confd, &recvhdr, sizeof(recvhdr), flags);

            if (recvd_bytes < 0)
            {
                sent_count--;
                flags = flags & (~MSG_PEEK);
                continue;
            }

            flags = flags & (~MSG_PEEK);
            recvd_bytes = recv(confd, &recvhdr, sizeof(recvhdr), flags);
            rcwin = recvhdr.wsize;
            printf("Received ACK: [%d] [%d]\n", recvhdr.ack_num, highest_ack_recvd);
            acks_recvd++; 

            if (recvhdr.seq_num < 0)
            {
                //
                // ACK for EOF received.
                //

                return;
            }

            if (recvhdr.ack_num > highest_ack_recvd)
            {
                highest_ack_recvd = recvhdr.ack_num;
            }

            make_socket_blocking(&confd);
            sent_count--;

        }while(acks_recvd < cwin_sent_count);

        stop_timer(&tout_val, &recvhdr);
        
        make_socket_blocking(&confd);
        remove_packets(packet_buffer_head, highest_ack_recvd, win);

        log_msg_int("highest_ack_recvd", highest_ack_recvd);

        if (packet_buffer_head->link == NULL || 
                (cwin_sent_count + win.win_l -1) == recvhdr.ack_num)
        {
            cwin_congestion = FALSE;
        }
        else
        {
            cwin_congestion = TRUE;
        }

        log_msg_int("cwin_congestion", cwin_congestion);

        slide_val = slide_window(packet_buffer_head, &win, highest_ack_recvd);
        printf("Window: LEFT :: %d RIGHT :: %d SIZE :: %d\n\n", win.win_l, win.win_r, win.win_size);
    }
}

